Dataset used¶

Public Domain.https://finance.yahoo.com/quote/JPM/history?p=JPM.

Objetivos¶

Esta análise de dados tem como único propósito a demonstração e teste da implementação básica de uma rede neural LSTM. Não deve ser interpretada como um conselho de investimento no mercado de ações, e não tem a intenção de prever valores de ações com alta precisão.

Dados e Imports¶

In [1]:
import pandas as pd
import numpy as np
import plotly.graph_objects as go
import matplotlib.pyplot as plt
import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, LSTM, Dropout, SimpleRNN
from tensorflow.keras.optimizers import SGD
import datetime as dt 
from sklearn.preprocessing import MinMaxScaler
import warnings
warnings.filterwarnings("ignore")
In [2]:
df1=pd.read_csv('JPM.csv')
df1.head()
Out[2]:
Date Open High Low Close Adj Close Volume
0 2013-07-22 56.169998 56.669998 55.950001 56.560001 42.783409 15999700
1 2013-07-23 56.720001 56.889999 56.470001 56.669998 42.866615 10577100
2 2013-07-24 56.919998 56.930000 56.380001 56.630001 42.836361 16665000
3 2013-07-25 56.450001 56.500000 55.889999 56.500000 42.738018 13870200
4 2013-07-26 56.060001 56.220001 55.599998 56.049999 42.397629 16762300

Checando Valores Ausentes¶

In [3]:
df1.isnull().sum()
Out[3]:
Date         0
Open         0
High         0
Low          0
Close        0
Adj Close    0
Volume       0
dtype: int64

Visualização dos Dados¶

In [4]:
prices = df1['Close']
In [5]:
fig = go.Figure(data=[go.Candlestick(x=df1['Date'],
                open=df1['Open'],
                high=df1['High'],
                low=df1['Low'],
                close=df1['Close'])])

fig.show()
In [6]:
## Visualizando valores de fechamento
plt.figure(figsize=(12,6))
plt.plot(df1['Close'])
plt.xticks(range(0,df1.shape[0],200), df1['Date'].loc[::200], rotation=45)
plt.xlabel('Datas', fontsize=15)
plt.ylabel('Preço Fechamento', fontsize=15)
plt.title('Histórico de Preço JP Morgan', fontsize=17)
plt.show()

Definindo os conjuntos de teste e de treino¶

In [19]:
#É interessante manter os dias anteriores (neste caso utilizei os 20 dias anteriores ao valor previsto), uma vez que o modelo
# tenta prever valores próximos.
time_step=20

#Utilizei 95% dos valores para treino do modelo, o resto para teste (representação gráfica abaixo).
train_size = int(len(prices) * 0.95)
test_size = len(prices) - train_size

#Definindo os dados de teste e treino de acordo com o tamanho acima.
# Além disso, transformei-os no tipo array do numpy para facilitar o uso da série temporal com a biblioteca TensorFlow.
train_data, test_data = np.array(prices[0:train_size]), np.array(prices[train_size - time_step:])
check_data = np.array(prices[train_size:])
In [18]:
test_data.shape
Out[18]:
(146,)
In [9]:
## Visualizando o intervalo de treinamento e o intervalo de teste:
train_xPlot= np.arange(0, len(train_data))
test_xPlot = np.arange(len(train_data),len(train_data)+len(test_data))

plt.figure(figsize=(12,6))
plt.plot(train_xPlot, train_data, color='blue', label='Train Data')
plt.plot(test_xPlot, test_data, color='green', label='Test Data')
plt.xticks(range(0,df1.shape[0],200), df1['Date'].loc[::200], rotation=45)
plt.xlabel('Datas')
plt.ylabel('Preço Medio')
plt.legend()
plt.show()

Normalizando os Dados¶

Normalizei os dados para evitar que alguns atributos com valores numericamente maiores dominem o processo de treinamento em detrimento de outros, que possuem valores numericamente menores. Assim, melhora-se o modelo como um todo.

In [10]:
#O método utilizado, neste caso, é o MinMaxScaler.
#Este método transforma os dados em uma escala entre um intervalo definido, que neste caso é entre 0 e 1.
scaler = MinMaxScaler(feature_range=(0,1))

#Utilizei o 'fit_transform' para ajustar o scale com o conjunto de treino.
train_data_norm = scaler.fit_transform(np.array(train_data).reshape(-1,1))
#Dessa forma, o de teste e o de checagem não precisam fazer o 'fit', apenas receber as transformações necessárias
test_data_norm=scaler.transform(np.array(test_data).reshape(-1,1))
check_data_norm = scaler.transform(np.array(check_data).reshape(-1,1))

Pré-processamento¶

Nesta etapa, feita a preparação ideal dos conjuntos de dados para treinamento, teste e validação da rede neural.

In [11]:
#Treino
X_train, y_train = [], []
for i in range(time_step, len(train_data)):
    X_train.append(train_data_norm[i-time_step:i])
    y_train.append(train_data_norm[i])

    
#Teste
X_test=[]
for i in range(time_step, time_step + len(check_data)):
    X_test.append(test_data_norm[i-time_step:i])
    

#Validação
X_check, y_check = [], []
for i in range(time_step, len(check_data_norm)):
    X_check.append(check_data_norm[i-time_step:i])
    y_check.append(check_data_norm[i])

#matriz numpy
X_train = np.array(X_train)
y_train= np.array(y_train)
X_test = np.array(X_test)
X_check = np.array(X_check)
y_check = np.array(y_check)

X_test.shape
Out[11]:
(126, 20, 1)

Treinando a RNN¶

In [12]:
#Rede neural profunda sequencial
model = Sequential()
model.add(LSTM(100, return_sequences=False, input_shape=(time_step,1)))
## semelhante ao sigmoid do LSTM (exercido aqui por Dense)
model.add(Dense(1))
model.compile(loss='mse', optimizer='adam')

model.summary()
Model: "sequential"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 lstm (LSTM)                 (None, 100)               40800     
                                                                 
 dense (Dense)               (None, 1)                 101       
                                                                 
=================================================================
Total params: 40,901
Trainable params: 40,901
Non-trainable params: 0
_________________________________________________________________
In [13]:
# Treinando o modelo
h = model.fit(X_train, y_train, validation_data=(X_check, y_check), epochs=30, batch_size=32)
Epoch 1/30
75/75 [==============================] - 3s 15ms/step - loss: 0.0080 - val_loss: 0.0011
Epoch 2/30
75/75 [==============================] - 1s 10ms/step - loss: 7.7747e-04 - val_loss: 0.0012
Epoch 3/30
75/75 [==============================] - 1s 10ms/step - loss: 7.4997e-04 - val_loss: 9.3597e-04
Epoch 4/30
75/75 [==============================] - 1s 10ms/step - loss: 6.8410e-04 - val_loss: 9.2730e-04
Epoch 5/30
75/75 [==============================] - 1s 10ms/step - loss: 6.5210e-04 - val_loss: 9.9417e-04
Epoch 6/30
75/75 [==============================] - 1s 10ms/step - loss: 6.4084e-04 - val_loss: 0.0020
Epoch 7/30
75/75 [==============================] - 1s 10ms/step - loss: 6.0964e-04 - val_loss: 8.4362e-04
Epoch 8/30
75/75 [==============================] - 1s 11ms/step - loss: 5.4071e-04 - val_loss: 7.5074e-04
Epoch 9/30
75/75 [==============================] - 1s 10ms/step - loss: 5.4867e-04 - val_loss: 7.4575e-04
Epoch 10/30
75/75 [==============================] - 1s 11ms/step - loss: 4.9040e-04 - val_loss: 6.8225e-04
Epoch 11/30
75/75 [==============================] - 1s 11ms/step - loss: 5.0460e-04 - val_loss: 7.0960e-04
Epoch 12/30
75/75 [==============================] - 1s 10ms/step - loss: 4.7567e-04 - val_loss: 6.5350e-04
Epoch 13/30
75/75 [==============================] - 1s 10ms/step - loss: 4.3930e-04 - val_loss: 6.4182e-04
Epoch 14/30
75/75 [==============================] - 1s 10ms/step - loss: 4.1386e-04 - val_loss: 6.7141e-04
Epoch 15/30
75/75 [==============================] - 1s 11ms/step - loss: 4.0710e-04 - val_loss: 5.9287e-04
Epoch 16/30
75/75 [==============================] - 1s 11ms/step - loss: 4.1017e-04 - val_loss: 6.5027e-04
Epoch 17/30
75/75 [==============================] - 1s 10ms/step - loss: 3.7765e-04 - val_loss: 5.7316e-04
Epoch 18/30
75/75 [==============================] - 1s 10ms/step - loss: 3.8240e-04 - val_loss: 0.0010
Epoch 19/30
75/75 [==============================] - 1s 10ms/step - loss: 3.6395e-04 - val_loss: 5.4218e-04
Epoch 20/30
75/75 [==============================] - 1s 11ms/step - loss: 3.5333e-04 - val_loss: 5.7757e-04
Epoch 21/30
75/75 [==============================] - 1s 11ms/step - loss: 3.5803e-04 - val_loss: 5.2111e-04
Epoch 22/30
75/75 [==============================] - 1s 10ms/step - loss: 3.6107e-04 - val_loss: 7.7913e-04
Epoch 23/30
75/75 [==============================] - 1s 11ms/step - loss: 3.4948e-04 - val_loss: 5.8040e-04
Epoch 24/30
75/75 [==============================] - 1s 11ms/step - loss: 3.5082e-04 - val_loss: 4.9181e-04
Epoch 25/30
75/75 [==============================] - 1s 11ms/step - loss: 3.3290e-04 - val_loss: 5.0026e-04
Epoch 26/30
75/75 [==============================] - 1s 10ms/step - loss: 3.3279e-04 - val_loss: 4.7841e-04
Epoch 27/30
75/75 [==============================] - 1s 10ms/step - loss: 3.1864e-04 - val_loss: 6.2307e-04
Epoch 28/30
75/75 [==============================] - 1s 10ms/step - loss: 3.1749e-04 - val_loss: 5.0682e-04
Epoch 29/30
75/75 [==============================] - 1s 10ms/step - loss: 3.0593e-04 - val_loss: 4.5198e-04
Epoch 30/30
75/75 [==============================] - 1s 10ms/step - loss: 3.2663e-04 - val_loss: 4.3569e-04
In [14]:
plt.plot(h.history["loss"],label='loss')
plt.plot(h.history["val_loss"],label='val_loss')
plt.legend()
plt.show()

Avaliação de Resultados¶

In [15]:
#predição de valores pela rede
predict = model.predict(X_test)

#transformação inversa do normalizador para utilizar os valores no gráfico
predict = scaler.inverse_transform(predict)
real = test_data
4/4 [==============================] - 0s 4ms/step
In [21]:
plt.figure(figsize=(12,6))
plt.plot(real,color='green',label='real')
plt.plot(predict, color='red',label='previsão')
plt.xticks(range(0,len(real), 20), df1['Date'].iloc[-len(real)::20],rotation=45)
plt.xlabel('Datas')
plt.ylabel('Preço Médio')
plt.title('Projeção Preços JP Morgan')
plt.legend()
plt.show()

Considerações Finais¶

Podemos observar que, mesmo quando um modelo é bem treinado e apresenta baixos valores de perda (loss) e perda de validação (val_loss), as previsões podem não ser suficientemente precisas para lidar com os preços das ações exclusivamente com base no algoritmo.